home *** CD-ROM | disk | FTP | other *** search
/ American Osteopathic Ass…tion Yearbook 2005 & 2006 / American Osteopathic Association Yearbook 2005 & 2006.iso / mac / app / urllib2.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2004-03-22  |  53.5 KB  |  1,224 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.3)
  3.  
  4. '''An extensible library for opening URLs using a variety of protocols
  5.  
  6. The simplest way to use this module is to call the urlopen function,
  7. which accepts a string containing a URL or a Request object (described
  8. below).  It opens the URL and returns the results as file-like
  9. object; the returned object has some extra methods described below.
  10.  
  11. The OpenerDirector manages a collection of Handler objects that do
  12. all the actual work.  Each Handler implements a particular protocol or
  13. option.  The OpenerDirector is a composite object that invokes the
  14. Handlers needed to open the requested URL.  For example, the
  15. HTTPHandler performs HTTP GET and POST requests and deals with
  16. non-error returns.  The HTTPRedirectHandler automatically deals with
  17. HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler
  18. deals with digest authentication.
  19.  
  20. urlopen(url, data=None) -- basic usage is that same as original
  21. urllib.  pass the url and optionally data to post to an HTTP URL, and
  22. get a file-like object back.  One difference is that you can also pass
  23. a Request instance instead of URL.  Raises a URLError (subclass of
  24. IOError); for HTTP errors, raises an HTTPError, which can also be
  25. treated as a valid response.
  26.  
  27. build_opener -- function that creates a new OpenerDirector instance.
  28. will install the default handlers.  accepts one or more Handlers as
  29. arguments, either instances or Handler classes that it will
  30. instantiate.  if one of the argument is a subclass of the default
  31. handler, the argument will be installed instead of the default.
  32.  
  33. install_opener -- installs a new opener as the default opener.
  34.  
  35. objects of interest:
  36. OpenerDirector --
  37.  
  38. Request -- an object that encapsulates the state of a request.  the
  39. state can be a simple as the URL.  it can also include extra HTTP
  40. headers, e.g. a User-Agent.
  41.  
  42. BaseHandler --
  43.  
  44. exceptions:
  45. URLError-- a subclass of IOError, individual protocols have their own
  46. specific subclass
  47.  
  48. HTTPError-- also a valid HTTP response, so you can treat an HTTP error
  49. as an exceptional event or valid response
  50.  
  51. internals:
  52. BaseHandler and parent
  53. _call_chain conventions
  54.  
  55. Example usage:
  56.  
  57. import urllib2
  58.  
  59. # set up authentication info
  60. authinfo = urllib2.HTTPBasicAuthHandler()
  61. authinfo.add_password(\'realm\', \'host\', \'username\', \'password\')
  62.  
  63. proxy_support = urllib2.ProxyHandler({"http" : "http://ahad-haam:3128"})
  64.  
  65. # build a new opener that adds authentication and caching FTP handlers
  66. opener = urllib2.build_opener(proxy_support, authinfo, urllib2.CacheFTPHandler)
  67.  
  68. # install it
  69. urllib2.install_opener(opener)
  70.  
  71. f = urllib2.urlopen(\'http://www.python.org/\')
  72.  
  73.  
  74. '''
  75. import socket
  76. import httplib
  77. import inspect
  78. import re
  79. import base64
  80. import urlparse
  81. import md5
  82. import mimetypes
  83. import mimetools
  84. import rfc822
  85. import ftplib
  86. import sys
  87. import time
  88. import os
  89. import gopherlib
  90. import posixpath
  91.  
  92. try:
  93.     from cStringIO import StringIO
  94. except ImportError:
  95.     from StringIO import StringIO
  96.  
  97.  
  98. try:
  99.     import sha
  100. except ImportError:
  101.     sha = None
  102.  
  103. from urllib import unwrap, unquote, splittype, splithost, addinfourl, splitport, splitgophertype, splitquery, splitattr, ftpwrapper, noheaders
  104. from urllib import getproxies
  105. from urllib import localhost, url2pathname
  106. __version__ = '2.0a1'
  107. _opener = None
  108.  
  109. def urlopen(url, data = None):
  110.     global _opener
  111.     if _opener is None:
  112.         _opener = build_opener()
  113.     
  114.     return _opener.open(url, data)
  115.  
  116.  
  117. def install_opener(opener):
  118.     global _opener
  119.     _opener = opener
  120.  
  121.  
  122. class URLError(IOError):
  123.     
  124.     def __init__(self, reason):
  125.         self.reason = reason
  126.  
  127.     
  128.     def __str__(self):
  129.         return '<urlopen error %s>' % self.reason
  130.  
  131.  
  132.  
  133. class HTTPError(URLError, addinfourl):
  134.     '''Raised when HTTP error occurs, but also acts like non-error return'''
  135.     __super_init = addinfourl.__init__
  136.     
  137.     def __init__(self, url, code, msg, hdrs, fp):
  138.         self.code = code
  139.         self.msg = msg
  140.         self.hdrs = hdrs
  141.         self.fp = fp
  142.         self.filename = url
  143.         if fp is not None:
  144.             self._HTTPError__super_init(fp, hdrs, url)
  145.         
  146.  
  147.     
  148.     def __str__(self):
  149.         return 'HTTP Error %s: %s' % (self.code, self.msg)
  150.  
  151.     
  152.     def __del__(self):
  153.         if self.fp:
  154.             self.fp.close()
  155.         
  156.  
  157.  
  158.  
  159. class GopherError(URLError):
  160.     pass
  161.  
  162.  
  163. class Request:
  164.     
  165.     def __init__(self, url, data = None, headers = { }):
  166.         self._Request__original = unwrap(url)
  167.         self.type = None
  168.         self.host = None
  169.         self.port = None
  170.         self.data = data
  171.         self.headers = { }
  172.         for key, value in headers.items():
  173.             self.add_header(key, value)
  174.         
  175.  
  176.     
  177.     def __getattr__(self, attr):
  178.         if attr[:12] == '_Request__r_':
  179.             name = attr[12:]
  180.             if hasattr(Request, 'get_' + name):
  181.                 getattr(self, 'get_' + name)()
  182.                 return getattr(self, attr)
  183.             
  184.         
  185.         raise AttributeError, attr
  186.  
  187.     
  188.     def get_method(self):
  189.         if self.has_data():
  190.             return 'POST'
  191.         else:
  192.             return 'GET'
  193.  
  194.     
  195.     def add_data(self, data):
  196.         self.data = data
  197.  
  198.     
  199.     def has_data(self):
  200.         return self.data is not None
  201.  
  202.     
  203.     def get_data(self):
  204.         return self.data
  205.  
  206.     
  207.     def get_full_url(self):
  208.         return self._Request__original
  209.  
  210.     
  211.     def get_type(self):
  212.         if self.type is None:
  213.             (self.type, self._Request__r_type) = splittype(self._Request__original)
  214.             if self.type is None:
  215.                 raise ValueError, 'unknown url type: %s' % self._Request__original
  216.             
  217.         
  218.         return self.type
  219.  
  220.     
  221.     def get_host(self):
  222.         if self.host is None:
  223.             (self.host, self._Request__r_host) = splithost(self._Request__r_type)
  224.             if self.host:
  225.                 self.host = unquote(self.host)
  226.             
  227.         
  228.         return self.host
  229.  
  230.     
  231.     def get_selector(self):
  232.         return self._Request__r_host
  233.  
  234.     
  235.     def set_proxy(self, host, type):
  236.         (self.host, self.type) = (host, type)
  237.         self._Request__r_host = self._Request__original
  238.  
  239.     
  240.     def add_header(self, key, val):
  241.         self.headers[key.capitalize()] = val
  242.  
  243.  
  244.  
  245. class OpenerDirector:
  246.     
  247.     def __init__(self):
  248.         server_version = 'Python-urllib/%s' % __version__
  249.         self.addheaders = [
  250.             ('User-agent', server_version)]
  251.         self.handlers = []
  252.         self.handle_open = { }
  253.         self.handle_error = { }
  254.  
  255.     
  256.     def add_handler(self, handler):
  257.         added = 0
  258.         for meth in dir(handler):
  259.             if meth[-5:] == '_open':
  260.                 protocol = meth[:-5]
  261.                 if protocol in self.handle_open:
  262.                     self.handle_open[protocol].append(handler)
  263.                     self.handle_open[protocol].sort()
  264.                 else:
  265.                     self.handle_open[protocol] = [
  266.                         handler]
  267.                 added = 1
  268.                 continue
  269.             
  270.             i = meth.find('_')
  271.             j = meth[i + 1:].find('_') + i + 1
  272.             if j != -1 and meth[i + 1:j] == 'error':
  273.                 proto = meth[:i]
  274.                 kind = meth[j + 1:]
  275.                 
  276.                 try:
  277.                     kind = int(kind)
  278.                 except ValueError:
  279.                     pass
  280.  
  281.                 dict = self.handle_error.get(proto, { })
  282.                 if kind in dict:
  283.                     dict[kind].append(handler)
  284.                     dict[kind].sort()
  285.                 else:
  286.                     dict[kind] = [
  287.                         handler]
  288.                 self.handle_error[proto] = dict
  289.                 added = 1
  290.                 continue
  291.                 continue
  292.         
  293.         if added:
  294.             self.handlers.append(handler)
  295.             self.handlers.sort()
  296.             handler.add_parent(self)
  297.         
  298.  
  299.     
  300.     def __del__(self):
  301.         self.close()
  302.  
  303.     
  304.     def close(self):
  305.         for handler in self.handlers:
  306.             handler.close()
  307.         
  308.         self.handlers = []
  309.  
  310.     
  311.     def _call_chain(self, chain, kind, meth_name, *args):
  312.         handlers = chain.get(kind, ())
  313.         for handler in handlers:
  314.             func = getattr(handler, meth_name)
  315.             result = func(*args)
  316.             if result is not None:
  317.                 return result
  318.                 continue
  319.         
  320.  
  321.     
  322.     def open(self, fullurl, data = None):
  323.         if isinstance(fullurl, basestring):
  324.             req = Request(fullurl, data)
  325.         else:
  326.             req = fullurl
  327.             if data is not None:
  328.                 req.add_data(data)
  329.             
  330.         result = self._call_chain(self.handle_open, 'default', 'default_open', req)
  331.         if result:
  332.             return result
  333.         
  334.         type_ = req.get_type()
  335.         result = self._call_chain(self.handle_open, type_, type_ + '_open', req)
  336.         if result:
  337.             return result
  338.         
  339.         return self._call_chain(self.handle_open, 'unknown', 'unknown_open', req)
  340.  
  341.     
  342.     def error(self, proto, *args):
  343.         if proto in [
  344.             'http',
  345.             'https']:
  346.             dict = self.handle_error['http']
  347.             proto = args[2]
  348.             meth_name = 'http_error_%d' % proto
  349.             http_err = 1
  350.             orig_args = args
  351.         else:
  352.             dict = self.handle_error
  353.             meth_name = proto + '_error'
  354.             http_err = 0
  355.         args = (dict, proto, meth_name) + args
  356.         result = self._call_chain(*args)
  357.         if result:
  358.             return result
  359.         
  360.         if http_err:
  361.             args = (dict, 'default', 'http_error_default') + orig_args
  362.             return self._call_chain(*args)
  363.         
  364.  
  365.  
  366.  
  367. def build_opener(*handlers):
  368.     '''Create an opener object from a list of handlers.
  369.  
  370.     The opener will use several default handlers, including support
  371.     for HTTP and FTP.
  372.  
  373.     If any of the handlers passed as arguments are subclasses of the
  374.     default handlers, the default handlers will not be used.
  375.     '''
  376.     opener = OpenerDirector()
  377.     default_classes = [
  378.         ProxyHandler,
  379.         UnknownHandler,
  380.         HTTPHandler,
  381.         HTTPDefaultErrorHandler,
  382.         HTTPRedirectHandler,
  383.         FTPHandler,
  384.         FileHandler]
  385.     if hasattr(httplib, 'HTTPS'):
  386.         default_classes.append(HTTPSHandler)
  387.     
  388.     skip = []
  389.     for klass in default_classes:
  390.         for check in handlers:
  391.             if inspect.isclass(check):
  392.                 if issubclass(check, klass):
  393.                     skip.append(klass)
  394.                 
  395.             issubclass(check, klass)
  396.             if isinstance(check, klass):
  397.                 skip.append(klass)
  398.                 continue
  399.         
  400.     
  401.     for klass in skip:
  402.         default_classes.remove(klass)
  403.     
  404.     for klass in default_classes:
  405.         opener.add_handler(klass())
  406.     
  407.     for h in handlers:
  408.         if inspect.isclass(h):
  409.             h = h()
  410.         
  411.         opener.add_handler(h)
  412.     
  413.     return opener
  414.  
  415.  
  416. class BaseHandler:
  417.     handler_order = 500
  418.     
  419.     def add_parent(self, parent):
  420.         self.parent = parent
  421.  
  422.     
  423.     def close(self):
  424.         self.parent = None
  425.  
  426.     
  427.     def __lt__(self, other):
  428.         if not hasattr(other, 'handler_order'):
  429.             return True
  430.         
  431.         return self.handler_order < other.handler_order
  432.  
  433.  
  434.  
  435. class HTTPDefaultErrorHandler(BaseHandler):
  436.     
  437.     def http_error_default(self, req, fp, code, msg, hdrs):
  438.         raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
  439.  
  440.  
  441.  
  442. class HTTPRedirectHandler(BaseHandler):
  443.     
  444.     def redirect_request(self, req, fp, code, msg, headers, newurl):
  445.         """Return a Request or None in response to a redirect.
  446.  
  447.         This is called by the http_error_30x methods when a
  448.         redirection response is received.  If a redirection should
  449.         take place, return a new Request to allow http_error_30x to
  450.         perform the redirect.  Otherwise, raise HTTPError if no-one
  451.         else should try to handle this url.  Return None if you can't
  452.         but another Handler might.
  453.         """
  454.         m = req.get_method()
  455.         if code in (301, 302, 303, 307) and m in ('GET', 'HEAD') and code in (301, 302, 303) and m == 'POST':
  456.             return Request(newurl, headers = req.headers)
  457.         else:
  458.             raise HTTPError(req.get_full_url(), code, msg, headers, fp)
  459.  
  460.     
  461.     def http_error_302(self, req, fp, code, msg, headers):
  462.         if 'location' in headers:
  463.             newurl = headers['location']
  464.         elif 'uri' in headers:
  465.             newurl = headers['uri']
  466.         else:
  467.             return None
  468.         newurl = urlparse.urljoin(req.get_full_url(), newurl)
  469.         new = self.redirect_request(req, fp, code, msg, headers, newurl)
  470.         if new is None:
  471.             return None
  472.         
  473.         new.error_302_dict = { }
  474.         if hasattr(req, 'error_302_dict'):
  475.             if len(req.error_302_dict) > 10 or newurl in req.error_302_dict:
  476.                 raise HTTPError(req.get_full_url(), code, self.inf_msg + msg, headers, fp)
  477.             
  478.             new.error_302_dict.update(req.error_302_dict)
  479.         
  480.         new.error_302_dict[newurl] = newurl
  481.         fp.read()
  482.         fp.close()
  483.         return self.parent.open(new)
  484.  
  485.     http_error_301 = http_error_303 = http_error_307 = http_error_302
  486.     inf_msg = 'The HTTP server returned a redirect error that would lead to an infinite loop.\nThe last 30x error message was:\n'
  487.  
  488.  
  489. class ProxyHandler(BaseHandler):
  490.     handler_order = 100
  491.     
  492.     def __init__(self, proxies = None):
  493.         if proxies is None:
  494.             proxies = getproxies()
  495.         
  496.         if not hasattr(proxies, 'has_key'):
  497.             raise AssertionError, 'proxies must be a mapping'
  498.         self.proxies = proxies
  499.         for type, url in proxies.items():
  500.             setattr(self, '%s_open' % type, (lambda r, proxy = url, type = type, meth = self.proxy_open: meth(r, proxy, type)))
  501.         
  502.  
  503.     
  504.     def proxy_open(self, req, proxy, type):
  505.         orig_type = req.get_type()
  506.         (type, r_type) = splittype(proxy)
  507.         (host, XXX) = splithost(r_type)
  508.         if '@' in host:
  509.             (user_pass, host) = host.split('@', 1)
  510.             if ':' in user_pass:
  511.                 (user, password) = user_pass.split(':', 1)
  512.                 user_pass = base64.encodestring('%s:%s' % (unquote(user), unquote(password)))
  513.                 req.add_header('Proxy-authorization', 'Basic ' + user_pass)
  514.             
  515.         
  516.         host = unquote(host)
  517.         req.set_proxy(host, type)
  518.         if orig_type == type:
  519.             return None
  520.         else:
  521.             return self.parent.open(req)
  522.  
  523.  
  524.  
  525. class CustomProxy:
  526.     
  527.     def __init__(self, proto, func = None, proxy_addr = None):
  528.         self.proto = proto
  529.         self.func = func
  530.         self.addr = proxy_addr
  531.  
  532.     
  533.     def handle(self, req):
  534.         if self.func and self.func(req):
  535.             return 1
  536.         
  537.  
  538.     
  539.     def get_proxy(self):
  540.         return self.addr
  541.  
  542.  
  543.  
  544. class CustomProxyHandler(BaseHandler):
  545.     handler_order = 100
  546.     
  547.     def __init__(self, *proxies):
  548.         self.proxies = { }
  549.  
  550.     
  551.     def proxy_open(self, req):
  552.         proto = req.get_type()
  553.         
  554.         try:
  555.             proxies = self.proxies[proto]
  556.         except KeyError:
  557.             return None
  558.  
  559.         for p in proxies:
  560.             if p.handle(req):
  561.                 req.set_proxy(p.get_proxy())
  562.                 return self.parent.open(req)
  563.                 continue
  564.         
  565.         return None
  566.  
  567.     
  568.     def do_proxy(self, p, req):
  569.         return self.parent.open(req)
  570.  
  571.     
  572.     def add_proxy(self, cpo):
  573.         if cpo.proto in self.proxies:
  574.             self.proxies[cpo.proto].append(cpo)
  575.         else:
  576.             self.proxies[cpo.proto] = [
  577.                 cpo]
  578.  
  579.  
  580.  
  581. class HTTPPasswordMgr:
  582.     
  583.     def __init__(self):
  584.         self.passwd = { }
  585.  
  586.     
  587.     def add_password(self, realm, uri, user, passwd):
  588.         if isinstance(uri, basestring):
  589.             uri = [
  590.                 uri]
  591.         
  592.         uri = tuple(map(self.reduce_uri, uri))
  593.         if not (realm in self.passwd):
  594.             self.passwd[realm] = { }
  595.         
  596.         self.passwd[realm][uri] = (user, passwd)
  597.  
  598.     
  599.     def find_user_password(self, realm, authuri):
  600.         domains = self.passwd.get(realm, { })
  601.         authuri = self.reduce_uri(authuri)
  602.         for uris, authinfo in domains.iteritems():
  603.             for uri in uris:
  604.                 if self.is_suburi(uri, authuri):
  605.                     return authinfo
  606.                     continue
  607.             
  608.         
  609.         return (None, None)
  610.  
  611.     
  612.     def reduce_uri(self, uri):
  613.         '''Accept netloc or URI and extract only the netloc and path'''
  614.         parts = urlparse.urlparse(uri)
  615.         if parts[1]:
  616.             if not parts[2]:
  617.                 pass
  618.             return (parts[1], '/')
  619.         else:
  620.             return (parts[2], '/')
  621.  
  622.     
  623.     def is_suburi(self, base, test):
  624.         '''Check if test is below base in a URI tree
  625.  
  626.         Both args must be URIs in reduced form.
  627.         '''
  628.         if base == test:
  629.             return True
  630.         
  631.         if base[0] != test[0]:
  632.             return False
  633.         
  634.         common = posixpath.commonprefix((base[1], test[1]))
  635.         if len(common) == len(base[1]):
  636.             return True
  637.         
  638.         return False
  639.  
  640.  
  641.  
  642. class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr):
  643.     
  644.     def find_user_password(self, realm, authuri):
  645.         (user, password) = HTTPPasswordMgr.find_user_password(self, realm, authuri)
  646.         if user is not None:
  647.             return (user, password)
  648.         
  649.         return HTTPPasswordMgr.find_user_password(self, None, authuri)
  650.  
  651.  
  652.  
  653. class AbstractBasicAuthHandler:
  654.     rx = re.compile('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', re.I)
  655.     
  656.     def __init__(self, password_mgr = None):
  657.         if password_mgr is None:
  658.             password_mgr = HTTPPasswordMgr()
  659.         
  660.         self.passwd = password_mgr
  661.         self.add_password = self.passwd.add_password
  662.  
  663.     
  664.     def http_error_auth_reqed(self, authreq, host, req, headers):
  665.         authreq = headers.get(authreq, None)
  666.         if authreq:
  667.             mo = AbstractBasicAuthHandler.rx.match(authreq)
  668.             if mo:
  669.                 (scheme, realm) = mo.groups()
  670.                 if scheme.lower() == 'basic':
  671.                     return self.retry_http_basic_auth(host, req, realm)
  672.                 
  673.             
  674.         
  675.  
  676.     
  677.     def retry_http_basic_auth(self, host, req, realm):
  678.         (user, pw) = self.passwd.find_user_password(realm, host)
  679.         if pw:
  680.             raw = '%s:%s' % (user, pw)
  681.             auth = 'Basic %s' % base64.encodestring(raw).strip()
  682.             if req.headers.get(self.auth_header, None) == auth:
  683.                 return None
  684.             
  685.             req.add_header(self.auth_header, auth)
  686.             return self.parent.open(req)
  687.         else:
  688.             return None
  689.  
  690.  
  691.  
  692. class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
  693.     auth_header = 'Authorization'
  694.     
  695.     def http_error_401(self, req, fp, code, msg, headers):
  696.         host = urlparse.urlparse(req.get_full_url())[1]
  697.         return self.http_error_auth_reqed('www-authenticate', host, req, headers)
  698.  
  699.  
  700.  
  701. class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
  702.     auth_header = 'Proxy-authorization'
  703.     
  704.     def http_error_407(self, req, fp, code, msg, headers):
  705.         host = req.get_host()
  706.         return self.http_error_auth_reqed('proxy-authenticate', host, req, headers)
  707.  
  708.  
  709.  
  710. class AbstractDigestAuthHandler:
  711.     
  712.     def __init__(self, passwd = None):
  713.         if passwd is None:
  714.             passwd = HTTPPasswordMgr()
  715.         
  716.         self.passwd = passwd
  717.         self.add_password = self.passwd.add_password
  718.  
  719.     
  720.     def http_error_auth_reqed(self, authreq, host, req, headers):
  721.         authreq = headers.get(self.auth_header, None)
  722.         if authreq:
  723.             kind = authreq.split()[0]
  724.             if kind == 'Digest':
  725.                 return self.retry_http_digest_auth(req, authreq)
  726.             
  727.         
  728.  
  729.     
  730.     def retry_http_digest_auth(self, req, auth):
  731.         (token, challenge) = auth.split(' ', 1)
  732.         chal = parse_keqv_list(parse_http_list(challenge))
  733.         auth = self.get_authorization(req, chal)
  734.         if auth:
  735.             auth_val = 'Digest %s' % auth
  736.             if req.headers.get(self.auth_header, None) == auth_val:
  737.                 return None
  738.             
  739.             req.add_header(self.auth_header, auth_val)
  740.             resp = self.parent.open(req)
  741.             return resp
  742.         
  743.  
  744.     
  745.     def get_authorization(self, req, chal):
  746.         
  747.         try:
  748.             realm = chal['realm']
  749.             nonce = chal['nonce']
  750.             algorithm = chal.get('algorithm', 'MD5')
  751.             opaque = chal.get('opaque', None)
  752.         except KeyError:
  753.             return None
  754.  
  755.         (H, KD) = self.get_algorithm_impls(algorithm)
  756.         if H is None:
  757.             return None
  758.         
  759.         (user, pw) = self.passwd.find_user_password(realm, req.get_full_url())
  760.         if user is None:
  761.             return None
  762.         
  763.         if req.has_data():
  764.             entdig = self.get_entity_digest(req.get_data(), chal)
  765.         else:
  766.             entdig = None
  767.         A1 = '%s:%s:%s' % (user, realm, pw)
  768.         if not req.has_data() and 'POST':
  769.             pass
  770.         A2 = '%s:%s' % ('GET', req.get_selector())
  771.         respdig = KD(H(A1), '%s:%s' % (nonce, H(A2)))
  772.         base = 'username="%s", realm="%s", nonce="%s", uri="%s", response="%s"' % (user, realm, nonce, req.get_selector(), respdig)
  773.         if opaque:
  774.             base = base + ', opaque="%s"' % opaque
  775.         
  776.         if entdig:
  777.             base = base + ', digest="%s"' % entdig
  778.         
  779.         if algorithm != 'MD5':
  780.             base = base + ', algorithm="%s"' % algorithm
  781.         
  782.         return base
  783.  
  784.     
  785.     def get_algorithm_impls(self, algorithm):
  786.         if algorithm == 'MD5':
  787.             
  788.             H = lambda x, e = encode_digest: e(md5.new(x).digest())
  789.         elif algorithm == 'SHA':
  790.             
  791.             H = lambda x, e = encode_digest: e(sha.new(x).digest())
  792.         
  793.         
  794.         KD = lambda s, d, H = H: H('%s:%s' % (s, d))
  795.         return (H, KD)
  796.  
  797.     
  798.     def get_entity_digest(self, data, chal):
  799.         return None
  800.  
  801.  
  802.  
  803. class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
  804.     '''An authentication protocol defined by RFC 2069
  805.  
  806.     Digest authentication improves on basic authentication because it
  807.     does not transmit passwords in the clear.
  808.     '''
  809.     auth_header = 'Authorization'
  810.     
  811.     def http_error_401(self, req, fp, code, msg, headers):
  812.         host = urlparse.urlparse(req.get_full_url())[1]
  813.         self.http_error_auth_reqed('www-authenticate', host, req, headers)
  814.  
  815.  
  816.  
  817. class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler):
  818.     auth_header = 'Proxy-Authorization'
  819.     
  820.     def http_error_407(self, req, fp, code, msg, headers):
  821.         host = req.get_host()
  822.         self.http_error_auth_reqed('proxy-authenticate', host, req, headers)
  823.  
  824.  
  825.  
  826. def encode_digest(digest):
  827.     hexrep = []
  828.     for c in digest:
  829.         n = ord(c) >> 4 & 15
  830.         hexrep.append(hex(n)[-1])
  831.         n = ord(c) & 15
  832.         hexrep.append(hex(n)[-1])
  833.     
  834.     return ''.join(hexrep)
  835.  
  836.  
  837. class AbstractHTTPHandler(BaseHandler):
  838.     
  839.     def do_open(self, http_class, req):
  840.         host = req.get_host()
  841.         if not host:
  842.             raise URLError('no host given')
  843.         
  844.         h = http_class(host)
  845.         if req.has_data():
  846.             data = req.get_data()
  847.             h.putrequest('POST', req.get_selector())
  848.             if not ('Content-type' in req.headers):
  849.                 h.putheader('Content-type', 'application/x-www-form-urlencoded')
  850.             
  851.             if not ('Content-length' in req.headers):
  852.                 h.putheader('Content-length', '%d' % len(data))
  853.             
  854.         else:
  855.             h.putrequest('GET', req.get_selector())
  856.         (scheme, sel) = splittype(req.get_selector())
  857.         (sel_host, sel_path) = splithost(sel)
  858.         if not sel_host:
  859.             pass
  860.         h.putheader('Host', host)
  861.         for name, value in self.parent.addheaders:
  862.             name = name.capitalize()
  863.             if name not in req.headers:
  864.                 h.putheader(name, value)
  865.                 continue
  866.         
  867.         for k, v in req.headers.items():
  868.             h.putheader(k, v)
  869.         
  870.         
  871.         try:
  872.             h.endheaders()
  873.         except socket.error:
  874.             err = None
  875.             raise URLError(err)
  876.  
  877.         if req.has_data():
  878.             h.send(data)
  879.         
  880.         (code, msg, hdrs) = h.getreply()
  881.         fp = h.getfile()
  882.         if code == 200:
  883.             return addinfourl(fp, hdrs, req.get_full_url())
  884.         else:
  885.             return self.parent.error('http', req, fp, code, msg, hdrs)
  886.  
  887.  
  888.  
  889. class HTTPHandler(AbstractHTTPHandler):
  890.     
  891.     def http_open(self, req):
  892.         return self.do_open(httplib.HTTP, req)
  893.  
  894.  
  895. if hasattr(httplib, 'HTTPS'):
  896.     
  897.     class HTTPSHandler(AbstractHTTPHandler):
  898.         
  899.         def https_open(self, req):
  900.             return self.do_open(httplib.HTTPS, req)
  901.  
  902.  
  903.  
  904.  
  905. class UnknownHandler(BaseHandler):
  906.     
  907.     def unknown_open(self, req):
  908.         type = req.get_type()
  909.         raise URLError('unknown url type: %s' % type)
  910.  
  911.  
  912.  
  913. def parse_keqv_list(l):
  914.     '''Parse list of key=value strings where keys are not duplicated.'''
  915.     parsed = { }
  916.     for elt in l:
  917.         (k, v) = elt.split('=', 1)
  918.         if v[0] == '"' and v[-1] == '"':
  919.             v = v[1:-1]
  920.         
  921.         parsed[k] = v
  922.     
  923.     return parsed
  924.  
  925.  
  926. def parse_http_list(s):
  927.     '''Parse lists as described by RFC 2068 Section 2.
  928.  
  929.     In particular, parse comman-separated lists where the elements of
  930.     the list may include quoted-strings.  A quoted-string could
  931.     contain a comma.
  932.     '''
  933.     list = []
  934.     end = len(s)
  935.     i = 0
  936.     inquote = 0
  937.     start = 0
  938.     while i < end:
  939.         cur = s[i:]
  940.         c = cur.find(',')
  941.         q = cur.find('"')
  942.         if c == -1:
  943.             list.append(s[start:])
  944.             break
  945.         
  946.         if q == -1:
  947.             if inquote:
  948.                 raise ValueError, 'unbalanced quotes'
  949.             else:
  950.                 list.append(s[start:i + c])
  951.                 i = i + c + 1
  952.         
  953.         if inquote:
  954.             if q < c:
  955.                 list.append(s[start:i + c])
  956.                 i = i + c + 1
  957.                 start = i
  958.                 inquote = 0
  959.             else:
  960.                 i = i + q
  961.         q < c
  962.         if c < q:
  963.             list.append(s[start:i + c])
  964.             i = i + c + 1
  965.             start = i
  966.             continue
  967.         inquote = 1
  968.         i = i + q + 1
  969.     return map((lambda x: x.strip()), list)
  970.  
  971.  
  972. class FileHandler(BaseHandler):
  973.     
  974.     def file_open(self, req):
  975.         url = req.get_selector()
  976.         if url[:2] == '//' and url[2:3] != '/':
  977.             req.type = 'ftp'
  978.             return self.parent.open(req)
  979.         else:
  980.             return self.open_local_file(req)
  981.  
  982.     names = None
  983.     
  984.     def get_names(self):
  985.         if FileHandler.names is None:
  986.             FileHandler.names = (socket.gethostbyname('localhost'), socket.gethostbyname(socket.gethostname()))
  987.         
  988.         return FileHandler.names
  989.  
  990.     
  991.     def open_local_file(self, req):
  992.         host = req.get_host()
  993.         file = req.get_selector()
  994.         localfile = url2pathname(file)
  995.         stats = os.stat(localfile)
  996.         size = stats.st_size
  997.         modified = rfc822.formatdate(stats.st_mtime)
  998.         mtype = mimetypes.guess_type(file)[0]
  999.         if not mtype:
  1000.             pass
  1001.         headers = mimetools.Message(StringIO('Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % ('text/plain', size, modified)))
  1002.         if host:
  1003.             (host, port) = splitport(host)
  1004.         
  1005.         if not host and not port and socket.gethostbyname(host) in self.get_names():
  1006.             return addinfourl(open(localfile, 'rb'), headers, 'file:' + file)
  1007.         
  1008.         raise URLError('file not on local host')
  1009.  
  1010.  
  1011.  
  1012. class FTPHandler(BaseHandler):
  1013.     
  1014.     def ftp_open(self, req):
  1015.         host = req.get_host()
  1016.         if not host:
  1017.             raise IOError, ('ftp error', 'no host given')
  1018.         
  1019.         
  1020.         try:
  1021.             host = socket.gethostbyname(host)
  1022.         except socket.error:
  1023.             msg = None
  1024.             raise URLError(msg)
  1025.  
  1026.         (host, port) = splitport(host)
  1027.         if port is None:
  1028.             port = ftplib.FTP_PORT
  1029.         
  1030.         (path, attrs) = splitattr(req.get_selector())
  1031.         path = unquote(path)
  1032.         dirs = path.split('/')
  1033.         (dirs, file) = (dirs[:-1], dirs[-1])
  1034.         if dirs and not dirs[0]:
  1035.             dirs = dirs[1:]
  1036.         
  1037.         user = passwd = ''
  1038.         
  1039.         try:
  1040.             fw = self.connect_ftp(user, passwd, host, port, dirs)
  1041.             if not file and 'I':
  1042.                 pass
  1043.             type = 'D'
  1044.             for attr in attrs:
  1045.                 (attr, value) = splitattr(attr)
  1046.                 if attr.lower() == 'type' and value in ('a', 'A', 'i', 'I', 'd', 'D'):
  1047.                     type = value.upper()
  1048.                     continue
  1049.             
  1050.             (fp, retrlen) = fw.retrfile(file, type)
  1051.             headers = ''
  1052.             mtype = mimetypes.guess_type(req.get_full_url())[0]
  1053.             if mtype:
  1054.                 headers += 'Content-type: %s\n' % mtype
  1055.             
  1056.             if retrlen is not None and retrlen >= 0:
  1057.                 headers += 'Content-length: %d\n' % retrlen
  1058.             
  1059.             sf = StringIO(headers)
  1060.             headers = mimetools.Message(sf)
  1061.             return addinfourl(fp, headers, req.get_full_url())
  1062.         except ftplib.all_errors:
  1063.             msg = None
  1064.             raise IOError, ('ftp error', msg), sys.exc_info()[2]
  1065.  
  1066.  
  1067.     
  1068.     def connect_ftp(self, user, passwd, host, port, dirs):
  1069.         fw = ftpwrapper(user, passwd, host, port, dirs)
  1070.         return fw
  1071.  
  1072.  
  1073.  
  1074. class CacheFTPHandler(FTPHandler):
  1075.     
  1076.     def __init__(self):
  1077.         self.cache = { }
  1078.         self.timeout = { }
  1079.         self.soonest = 0
  1080.         self.delay = 60
  1081.         self.max_conns = 16
  1082.  
  1083.     
  1084.     def setTimeout(self, t):
  1085.         self.delay = t
  1086.  
  1087.     
  1088.     def setMaxConns(self, m):
  1089.         self.max_conns = m
  1090.  
  1091.     
  1092.     def connect_ftp(self, user, passwd, host, port, dirs):
  1093.         key = (user, passwd, host, port)
  1094.         if key in self.cache:
  1095.             self.timeout[key] = time.time() + self.delay
  1096.         else:
  1097.             self.cache[key] = ftpwrapper(user, passwd, host, port, dirs)
  1098.             self.timeout[key] = time.time() + self.delay
  1099.         self.check_cache()
  1100.         return self.cache[key]
  1101.  
  1102.     
  1103.     def check_cache(self):
  1104.         t = time.time()
  1105.         if self.soonest <= t:
  1106.             for k, v in self.timeout.items():
  1107.                 if v < t:
  1108.                     self.cache[k].close()
  1109.                     del self.cache[k]
  1110.                     del self.timeout[k]
  1111.                     continue
  1112.             
  1113.         
  1114.         self.soonest = min(self.timeout.values())
  1115.         if len(self.cache) == self.max_conns:
  1116.             for k, v in self.timeout.items():
  1117.                 if v == self.soonest:
  1118.                     del self.cache[k]
  1119.                     del self.timeout[k]
  1120.                     break
  1121.                     continue
  1122.             
  1123.             self.soonest = min(self.timeout.values())
  1124.         
  1125.  
  1126.  
  1127.  
  1128. class GopherHandler(BaseHandler):
  1129.     
  1130.     def gopher_open(self, req):
  1131.         host = req.get_host()
  1132.         if not host:
  1133.             raise GopherError('no host given')
  1134.         
  1135.         host = unquote(host)
  1136.         selector = req.get_selector()
  1137.         (type, selector) = splitgophertype(selector)
  1138.         (selector, query) = splitquery(selector)
  1139.         selector = unquote(selector)
  1140.         if query:
  1141.             query = unquote(query)
  1142.             fp = gopherlib.send_query(selector, query, host)
  1143.         else:
  1144.             fp = gopherlib.send_selector(selector, host)
  1145.         return addinfourl(fp, noheaders(), req.get_full_url())
  1146.  
  1147.  
  1148.  
  1149. class OpenerFactory:
  1150.     default_handlers = [
  1151.         UnknownHandler,
  1152.         HTTPHandler,
  1153.         HTTPDefaultErrorHandler,
  1154.         HTTPRedirectHandler,
  1155.         FTPHandler,
  1156.         FileHandler]
  1157.     handlers = []
  1158.     replacement_handlers = []
  1159.     
  1160.     def add_handler(self, h):
  1161.         self.handlers = self.handlers + [
  1162.             h]
  1163.  
  1164.     
  1165.     def replace_handler(self, h):
  1166.         pass
  1167.  
  1168.     
  1169.     def build_opener(self):
  1170.         opener = OpenerDirector()
  1171.         for ph in self.default_handlers:
  1172.             if inspect.isclass(ph):
  1173.                 ph = ph()
  1174.             
  1175.             opener.add_handler(ph)
  1176.         
  1177.  
  1178.  
  1179. if __name__ == '__main__':
  1180.     if socket.gethostname() == 'bitdiddle':
  1181.         localhost = 'bitdiddle.cnri.reston.va.us'
  1182.     elif socket.gethostname() == 'bitdiddle.concentric.net':
  1183.         localhost = 'localhost'
  1184.     else:
  1185.         localhost = None
  1186.     urls = [
  1187.         'gopher://gopher.lib.ncsu.edu/11/library/stacks/Alex',
  1188.         'gopher://gopher.vt.edu:10010/10/33',
  1189.         'file:/etc/passwd',
  1190.         'file://nonsensename/etc/passwd',
  1191.         'ftp://www.python.org/pub/python/misc/sousa.au',
  1192.         'ftp://www.python.org/pub/tmp/blat',
  1193.         'http://www.espn.com/',
  1194.         'http://www.python.org/Spanish/Inquistion/',
  1195.         ('http://www.python.org/cgi-bin/faqw.py', 'query=pythonistas&querytype=simple&casefold=yes&req=search'),
  1196.         'http://www.python.org/',
  1197.         'ftp://gatekeeper.research.compaq.com/pub/DEC/SRC/research-reports/00README-Legal-Rules-Regs']
  1198.     cfh = CacheFTPHandler()
  1199.     cfh.setTimeout(1)
  1200.     install_opener(build_opener(cfh, GopherHandler))
  1201.     for url in urls:
  1202.         if isinstance(url, tuple):
  1203.             (url, req) = url
  1204.         else:
  1205.             req = None
  1206.         print url
  1207.         
  1208.         try:
  1209.             f = urlopen(url, req)
  1210.         except IOError:
  1211.             err = None
  1212.             print 'IOError:', err
  1213.         except socket.error:
  1214.             err = None
  1215.             print 'socket.error:', err
  1216.  
  1217.         buf = f.read()
  1218.         f.close()
  1219.         print 'read %d bytes' % len(buf)
  1220.         print 
  1221.         time.sleep(0.10000000000000001)
  1222.     
  1223.  
  1224.